package io.gitee.mingbaobaba.apijson.querycondition.query.template;


import com.alibaba.fastjson.*;
import io.gitee.mingbaobaba.apijson.querycondition.query.conditions.ApiJsonPageInfo;
import io.gitee.mingbaobaba.apijson.querycondition.query.conditions.ApiJsonQuery;
import io.gitee.mingbaobaba.apijson.querycondition.query.dao.DwQueryDao;
import io.gitee.mingbaobaba.apijson.querycondition.query.template.support.LoopQueryConfig;
import io.gitee.mingbaobaba.apijson.querycondition.query.template.support.LoopQueryExecutor;
import io.gitee.mingbaobaba.apijson.querycondition.query.template.mapping.ResultMapper;
import io.gitee.mingbaobaba.apijson.querycondition.query.template.mapping.FastjsonResultMapper;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * <p>查询模板工具</p>
 *
 * @author yingsheng.ye
 * @version 1.0.0
 * @since 2023/6/20 5:46
 */
@Setter
@Slf4j
public class ApiJsonQueryTemplate {

    /**
     * 数仓查询接口
     */
    private DwQueryDao dao;

    private LoopQueryExecutor loopQueryExecutor;
    private ResultMapper resultMapper;

    public ApiJsonQueryTemplate() {
        this(new LoopQueryExecutor(), new FastjsonResultMapper());
    }

    public ApiJsonQueryTemplate(LoopQueryExecutor loopQueryExecutor) {
        this(loopQueryExecutor, new FastjsonResultMapper());
    }

    public ApiJsonQueryTemplate(LoopQueryExecutor loopQueryExecutor, ResultMapper resultMapper) {
        this.loopQueryExecutor = loopQueryExecutor;
        this.resultMapper = resultMapper;
    }

    /**
     * 查询列表服务
     *
     * @param query  查询条件
     * @param tClass 结果转换类
     * @param <T>    泛型
     * @return List<T>
     */
    public <T> List<T> getList(ApiJsonQuery query, Class<T> tClass) {
        return queryList(query, tClass, null, null);
    }

    /**
     * 查询列表服务
     *
     * @param query        查询条件
     * @param tClass       结果转换类
     * @param covertBefore 转换前操作
     * @param <T>          List<T>
     * @return 泛型
     */
    public <T> List<T> getList(ApiJsonQuery query, Class<T> tClass, Consumer<JSONObject> covertBefore) {
        return queryList(query, tClass, covertBefore, null);
    }

    /**
     * 查询列表服务
     *
     * @param query        查询条件
     * @param tClass       结果转换类
     * @param covertBefore 转换前操作
     * @param covertAfter  转换后操作
     * @param <T>          泛型
     * @return List<T>
     */
    public <T> List<T> getList(ApiJsonQuery query, Class<T> tClass, Consumer<JSONObject> covertBefore, BiConsumer<JSONObject, T> covertAfter) {
        return queryList(query, tClass, covertBefore, covertAfter);
    }

    /**
     * 查询列表服务
     *
     * @param query         查询条件
     * @param typeReference 结果转换类
     * @param <T>           泛型
     * @return List<T>
     */
    public <T> List<T> getList(ApiJsonQuery query, TypeReference<T> typeReference) {
        return queryList(query, typeReference, null, null);
    }

    /**
     * 查询列表服务
     *
     * @param query         查询条件
     * @param typeReference 结果转换类
     * @param covertBefore  转换前操作
     * @param <T>           泛型
     * @return List<T>
     */
    public <T> List<T> getList(ApiJsonQuery query, TypeReference<T> typeReference, Consumer<JSONObject> covertBefore) {
        return queryList(query, typeReference, covertBefore, null);
    }

    /**
     * 查询列表服务
     *
     * @param query         查询条件
     * @param typeReference 结果转换类
     * @param covertBefore  转换前操作
     * @param covertAfter   转换后操作
     * @param <T>           泛型
     * @return List<T>
     */
    public <T> List<T> getList(ApiJsonQuery query, TypeReference<T> typeReference, Consumer<JSONObject> covertBefore, BiConsumer<JSONObject, T> covertAfter) {
        return queryList(query, typeReference, covertBefore, covertAfter);
    }


    /**
     * 查询一条数据
     *
     * @param query  查询条件
     * @param tClass 结果转换类
     * @param <T>    泛型
     * @return T
     */
    public <T> T getOne(ApiJsonQuery query, Class<T> tClass) {
        return getOne(query, tClass, null);
    }

    /**
     * 查询一条数据
     *
     * @param query        查询条件
     * @param tClass       结果转换类
     * @param covertBefore 转换前操作
     * @param <T>          泛型
     * @return T
     */
    public <T> T getOne(ApiJsonQuery query, Class<T> tClass, Consumer<JSONObject> covertBefore) {
        return getOne(query, tClass, covertBefore, null);
    }

    /**
     * 查询一条数据
     *
     * @param query        查询条件
     * @param tClass       结果转换类
     * @param covertBefore 转换前操作
     * @param covertAfter  转换后操作
     * @param <T>          泛型
     * @return T
     */
    public <T> T getOne(ApiJsonQuery query, Class<T> tClass, Consumer<JSONObject> covertBefore, BiConsumer<JSONObject, T> covertAfter) {
        List<T> list = queryList(query, tClass, covertBefore, covertAfter);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        Assert.isTrue(list.size() == 1, "期望返回一条数据，但是结果返回多条数据");
        return list.get(0);
    }


    /**
     * 查询一条数据
     *
     * @param query         查询条件
     * @param typeReference 结果转换类
     * @param <T>           泛型
     * @return T
     */
    public <T> T getOne(ApiJsonQuery query, TypeReference<T> typeReference) {
        return getOne(query, typeReference, null, null);
    }

    /**
     * 查询一条数据
     *
     * @param query         查询条件
     * @param typeReference 结果转换类
     * @param covertBefore  转换前操作
     * @param <T>           泛型
     * @return T
     */
    public <T> T getOne(ApiJsonQuery query, TypeReference<T> typeReference, Consumer<JSONObject> covertBefore) {
        return getOne(query, typeReference, covertBefore, null);
    }

    /**
     * 查询一条数据
     *
     * @param query         查询条件
     * @param typeReference 结果转换类
     * @param covertBefore  转换前操作
     * @param covertAfter   转换后操作
     * @param <T>           泛型
     * @return T
     */
    public <T> T getOne(ApiJsonQuery query, TypeReference<T> typeReference, Consumer<JSONObject> covertBefore, BiConsumer<JSONObject, T> covertAfter) {
        List<T> list = queryList(query, typeReference, covertBefore, covertAfter);
        Assert.isTrue(list.size() == 1, "期望返回一条数据，但是结果返回多条数据");
        return list.get(0);
    }

    /**
     * 查询统计条数
     *
     * @param query 查询条件
     * @return 条数
     */
    public Long count(ApiJsonQuery query) {
        Map<String, Object> params = query.getApiJsonParam();
        params.put("@schema", query.getSchema());
        params.put("total@", "/[]/total");
        @SuppressWarnings("unchecked")
        Map<String, Object> modelParams = (Map<String, Object>) params.get("[]");
        if (Objects.nonNull(modelParams)) {
            //查询表中的所有数据
            modelParams.put("query", 1);
        }
        JSONObject data = resultToObject(dao.getData(params, query));
        return Objects.nonNull(data) ? data.getLong("total") : 0L;
    }

    /**
     * 分页查询结果
     *
     * @param query  查询条件
     * @param page   当前页
     * @param limit  分页条数
     * @param tClass 转换类
     * @param <T>    泛型
     * @return DwPageInfo<T>
     */
    public <T> ApiJsonPageInfo<T> getPageList(ApiJsonQuery query, Integer page, Integer limit, Class<T> tClass) {
        return queryPageList(query, page, limit, tClass, null, null);
    }

    /**
     * 分页查询结果
     *
     * @param query        查询条件
     * @param page         当前页
     * @param limit        分页条数
     * @param tClass       转换类
     * @param covertBefore 转换前操作
     * @param <T>          泛型
     * @return DwPageInfo<T>
     */
    public <T> ApiJsonPageInfo<T> getPageList(ApiJsonQuery query, Integer page, Integer limit, Class<T> tClass, Consumer<JSONObject> covertBefore) {
        return queryPageList(query, page, limit, tClass, covertBefore, null);
    }

    /**
     * 分页查询结果
     *
     * @param query        查询条件
     * @param page         当前页
     * @param limit        分页条数
     * @param tClass       转换类
     * @param covertBefore 转换前操作
     * @param covertAfter  转换后操作
     * @param <T>          泛型
     * @return DwPageInfo<T>
     */
    public <T> ApiJsonPageInfo<T> getPageList(ApiJsonQuery query, Integer page, Integer limit, Class<T> tClass, Consumer<JSONObject> covertBefore, BiConsumer<JSONObject, T> covertAfter) {
        return queryPageList(query, page, limit, tClass, covertBefore, covertAfter);
    }

    /**
     * 分页查询结果
     *
     * @param query         查询条件
     * @param page          当前页
     * @param limit         分页条数
     * @param typeReference 转换类
     * @param <T>           泛型
     * @return DwPageInfo<T>
     */
    public <T> ApiJsonPageInfo<T> getPageList(ApiJsonQuery query, Integer page, Integer limit, TypeReference<T> typeReference) {
        return queryPageList(query, page, limit, typeReference, null, null);
    }

    /**
     * 分页查询结果
     *
     * @param query         查询条件
     * @param page          当前页
     * @param limit         分页条数
     * @param typeReference 转换类
     * @param covertBefore  转换前操作
     * @param <T>           泛型
     * @return DwPageInfo<T>
     */
    public <T> ApiJsonPageInfo<T> getPageList(ApiJsonQuery query, Integer page, Integer limit, TypeReference<T> typeReference, Consumer<JSONObject> covertBefore) {
        return queryPageList(query, page, limit, typeReference, covertBefore, null);
    }

    /**
     * 分页查询结果
     *
     * @param query         查询条件
     * @param page          当前页
     * @param limit         分页条数
     * @param typeReference 转换类
     * @param covertBefore  转换前操作
     * @param covertAfter   转换后操作
     * @param <T>           泛型
     * @return DwPageInfo<T>
     */
    public <T> ApiJsonPageInfo<T> getPageList(ApiJsonQuery query, Integer page, Integer limit, TypeReference<T> typeReference, Consumer<JSONObject> covertBefore, BiConsumer<JSONObject, T> covertAfter) {
        return queryPageList(query, page, limit, typeReference, covertBefore, covertAfter);
    }

    /**
     * 分页查询结果
     *
     * @param query 查询条件
     * @param page  当前页
     * @param limit 分页条数
     * @param o     转换类
     * @param <T>   泛型
     * @return DwPageInfo<T>
     */
    private <T> ApiJsonPageInfo<T> queryPageList(ApiJsonQuery query, Integer page, Integer limit, Object o, Consumer<JSONObject> covertBefore, BiConsumer<JSONObject, T> covertAfter) {
        Map<String, Object> params = query.getApiJsonParam();
        //如果同时为空取参数构建中的page值
        if (null != page || null != limit) {
            if (null == page || page < 1) {
                page = 1;
            }
            if (null == limit || limit < 1) {
                limit = 10;
            }
            params.put("total@", "/[]/total");
            @SuppressWarnings("unchecked")
            Map<String, Object> modelParams = (Map<String, Object>) params.get("[]");
            //apiJson分页从0开始
            int currentPage = page - 1;
            //查询表中的所有数据
            modelParams.put("page", currentPage);
            modelParams.put("count", limit);
            modelParams.put("query", 2);
        }
        JSONObject data = resultToObject(dao.getData(params, query));
        ApiJsonPageInfo<T> dwPageInfo = new ApiJsonPageInfo<>();
        dwPageInfo.setData(praseResultToList(query, data, o, covertBefore, covertAfter));
        dwPageInfo.setPage(page);
        dwPageInfo.setLimit(limit);
        dwPageInfo.setTotal(data.containsKey("total") ? data.getLong("total") : 0L);
        return dwPageInfo;
    }


    /**
     * 查询列表服务
     *
     * @param query        查询条件
     * @param o            结果转换类
     * @param covertBefore 转换前
     * @param covertAfter  转换后
     * @param <T>          泛型
     * @return List<T>
     */
    public <T> List<T> queryList(ApiJsonQuery query, Object o, Consumer<JSONObject> covertBefore, BiConsumer<JSONObject, T> covertAfter) {
        Map<String, Object> params = query.getApiJsonParam();
        JSONObject data = resultToObject(dao.getData(params, query));
        return praseResultToList(query, data, o, covertBefore, covertAfter);
    }

    /**
     * 查询列表服务
     *
     * @param query         查询条件
     * @param typeReference 结果转换类
     * @param limit         指定单次循环条数
     * @param <T>           泛型
     * @return List<T>
     */
    public <T> List<T> queryListLoop(ApiJsonQuery query, TypeReference<T> typeReference, Integer limit) {
        return queryLoop(query, typeReference, LoopQueryConfig.of(limit), null, null);
    }

    /**
     * 查询列表服务
     *
     * @param query         查询条件
     * @param typeReference 结果转换类
     * @param limit         指定单次循环条数
     * @param maxRetCount   指定最大返回条数
     * @param <T>           泛型
     * @return List<T>
     */
    public <T> List<T> queryListLoop(ApiJsonQuery query, TypeReference<T> typeReference, Integer limit, Integer maxRetCount) {
        return queryLoop(query, typeReference, LoopQueryConfig.of(limit, maxRetCount), null, null);
    }


    /**
     * 查询列表服务
     *
     * @param query  查询条件
     * @param tClass 结果转换类
     * @param limit  指定单次循环条数
     * @param <T>    泛型
     * @return List<T>
     */
    public <T> List<T> queryListLoop(ApiJsonQuery query, Class<T> tClass, Integer limit) {
        return queryLoop(query, tClass, LoopQueryConfig.of(limit), null, null);
    }

    /**
     * 查询列表服务
     *
     * @param query       查询条件
     * @param tClass      结果转换类
     * @param limit       指定单次循环条数
     * @param maxRetCount 指定最大返回条数
     * @param <T>         泛型
     * @return List<T>
     */
    public <T> List<T> queryListLoop(ApiJsonQuery query, Class<T> tClass, Integer limit, Integer maxRetCount) {
        return queryLoop(query, tClass, LoopQueryConfig.of(limit, maxRetCount), null, null);
    }


    /**
     * 查询列表服务
     *
     * @param query        查询条件
     * @param o            结果转换类
     * @param covertBefore 转换前
     * @param covertAfter  转换后
     * @param <T>          泛型
     * @return List<T>
     */
    public <T> List<T> queryListLoop(ApiJsonQuery query, Object o, Consumer<JSONObject> covertBefore, BiConsumer<JSONObject, T> covertAfter) {
        return queryLoop(query, o, LoopQueryConfig.builder().build(), covertBefore, covertAfter);
    }

    /**
     * 查询列表服务
     *
     * @param query        查询条件
     * @param o            结果转换类
     * @param limit        指定轮询条数
     * @param covertBefore 转换前
     * @param covertAfter  转换后
     * @param <T>          泛型
     * @return List<T>
     */
    public <T> List<T> queryListLoop(ApiJsonQuery query, Object o, Integer limit, Consumer<JSONObject> covertBefore, BiConsumer<JSONObject, T> covertAfter) {
        return queryLoop(query, o, LoopQueryConfig.of(limit), covertBefore, covertAfter);
    }

    /**
     * 查询列表服务
     *
     * @param query        查询条件
     * @param o            结果转换类
     * @param limit        指定轮询条数
     * @param covertBefore 转换前
     * @param covertAfter  转换后
     * @param <T>          泛型
     * @return List<T>
     */
    public <T> List<T> queryListLoop(ApiJsonQuery query, Object o, Integer limit, Integer maxRetCount, Consumer<JSONObject> covertBefore, BiConsumer<JSONObject, T> covertAfter) {
        return queryLoop(query, o, LoopQueryConfig.of(limit, maxRetCount), covertBefore, covertAfter);
    }


    /**
     * 查询列表
     *
     * @param query        查询条件
     * @param o            返回类型
     * @param covertBefore 转换前
     * @param covertAfter  转换后
     * @param <T>          泛型
     */
    private <T> List<T> queryLoop(ApiJsonQuery query, Object o, LoopQueryConfig loopQueryConfig,
                                  Consumer<JSONObject> covertBefore, BiConsumer<JSONObject, T> covertAfter) {
        LoopQueryConfig config = loopQueryConfig == null ? LoopQueryConfig.builder().build() : loopQueryConfig;
        return getLoopQueryExecutor().execute(config, (page, limit) -> queryPageList(query, page, limit, o, covertBefore, covertAfter));
    }

    private LoopQueryExecutor getLoopQueryExecutor() {
        if (loopQueryExecutor == null) {
            loopQueryExecutor = new LoopQueryExecutor();
        }
        return loopQueryExecutor;
    }

    /**
     * 结果转换
     *
     * @param res 返回结果
     * @return JSONObject
     */
    private JSONObject resultToObject(String res) {
        return JSON.parseObject(res);
    }

    /**
     * 将结果转换为list
     *
     * @param query 查询条件
     * @param data  数据
     * @param o     类型
     * @param <T>   Class<T>
     * @return List<T>
     */
    private <T> List<T> praseResultToList(ApiJsonQuery query, JSONObject data, Object o, Consumer<JSONObject> covertBefore, BiConsumer<JSONObject, T> covertAfter) {
        //解析结果转义
        JSONArray jsonArray = data.getJSONArray("[]");
        List<T> list = new ArrayList<>();
        if (!CollectionUtils.isEmpty(jsonArray)) {
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject resultObj = jsonArray.getJSONObject(i).getJSONObject(query.getTable());
                if (StringUtils.isNotBlank(query.getJoinTable())) {
                    JSONObject relResultObj = jsonArray.getJSONObject(i).getJSONObject(query.getJoinTable());
                    if (relResultObj != null) {
                        for (String key : relResultObj.keySet()) {
                            String keyAlias = resultObj.containsKey(key) ? "_" + key : key;
                            resultObj.put(keyAlias, relResultObj.get(key));
                        }
                    }
                }
                if (null != covertBefore) {
                    covertBefore.accept(resultObj);
                }
                //结果对象
                T covertResultObj = convertResult(o, resultObj);
                if (null != covertAfter) {
                    covertAfter.accept(resultObj, covertResultObj);
                }
                list.add(covertResultObj);
            }
        }
        return list;
    }

    /**
     * 对象转换
     *
     * @param targetType        转换类
     * @param source 结果数据
     * @param <T>       泛型
     * @return T
     */
    @SuppressWarnings("unchecked")
    private <T> T convertResult(Object targetType, JSONObject source) {
        ResultMapper mapper = getResultMapper();
        if (targetType instanceof JSONObject) {
            return mapper.map(source, (JSONObject) targetType);
        }
        if (targetType instanceof TypeReference) {
            return mapper.map(source, (TypeReference<T>) targetType);
        }
        return mapper.map(source, (Class<T>) targetType);
    }

    private ResultMapper getResultMapper() {
        if (resultMapper == null) {
            resultMapper = new FastjsonResultMapper();
        }
        return resultMapper;
    }
}
